﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using POVRay;
using System.Diagnostics;

/* TODO: Why?
 * Instantly available and complete list of all legal properties and values inside a POV-Ray statement.
 * No need of knowledge about the proper order of statements and keywords.
 * Obvious types for values (integer, float, bool, color, vector, enumeration)
 * The whole power and elegance of C# to compute everything without the need to use the #-directives.
*/

namespace POVRay_Test {
    class Program {
        //static string povExePath = "C:\\Spiele\\POV-Ray\\v3.6\\bin\\pvengine64.exe";
        //static string povExePath = @"S:\Spiele\POV-Ray\v3.6\bin\pvengine-sse2.exe";
        static string povExePath = @"C:\Program Files\POV-Ray\v3.7\bin\pvengine64.exe";

        static void Main(string[] args) {
            Scene.GlobalOneLiners = false;
            
            Test1();
            //Test2();
        }

        static void Test1() {
            Scene scene = new Scene("test1");
            Scene.GlobalOneLiners = false;

            scene.GlobalSettings = new GlobalSettings(1.0f);
            //scene.Fog.

            //scene.Camera = new Camera(new Vector3(-1, 1, -10) * 1.0f, new Vector3(0, 0, 0));

            scene.Camera = new Camera(new Vector3(2, 6, 2) * 0.75f * 0.75f, new Vector3(0, 0, 0));

            //scene.Camera.Type = CameraType.Orthographic;
            //scene.Camera.Angle = 60;
            //scene.Camera.Transformation = new Transformation(new TransformationOperation[]{new Translation(-1, 0, 0) });
            //scene.Camera.Aperture = 0.5f;
            //scene.Camera.BlurSamples = 100;
            //scene.Camera.FocalPoint = new Vector3(0, 1.8f, 0) * 1;

            //awesome pigment pattern anti-aliasing!
            /*
            scene.Camera.Aperture = 0.0001f;
            scene.Camera.BlurSamples = 1000;
            */

            //Rainbow rainbow1 = new Rainbow(new Vector3(0, 0, 1), 10, 5, 500, new ColorMap(new Dictionary<float, Color>() { { 0.0f, new Color(1, 0, 0) }, { 0.5f, new Color(0, 1, 0) }, { 1.0f, new Color(0, 0, 1) } }));
            //scene.Rainbows.AddLast(rainbow1);

            scene.LightSources.AddLast(new LightSource(new Vector3(-20, 40, 0), new Color(1, 1, 1)));

            var floorPigment = Pigment.Checker(Color.FromHSB(1, 0.9f, 0.5f), Color.FromHSB(0.1f, 0.9f, 0.5f));
            floorPigment.Transformation = new Scale(0.5f);

            Plane plane = new Plane(new Vector3(0, 1, 0), 0);
            plane.Pigment = floorPigment;
            plane.Finish = new Finish();
            scene.Objects.AddLast(plane);

            Sphere sphere = new Sphere(new Vector3(0, 0.5f, 0), 0.5f);
            sphere.Pigment = Pigment.Checker(Color.FromHSB(0.10f, 0.99f, 0.9f, 0.5f), Color.FromHSB(0.02f, 0.99f, 0.9f, 0.8f));
            sphere.Pigment.Transformation = new Scale(0.3f);
            sphere.Transformation = new Scale(2);
            sphere.Finish = new Finish();
            sphere.Finish.Specular = 0.3f;
            //sphere.Finish.ConserveEnergy = true;
            sphere.Finish.Reflection = new Reflection(0.3f, 0.6f, true, null, null, 0.8f);


            //sphere.Photons = new ObjectPhotons(1, true, true, null, false);
            //sphere.Finish.Iridescence = new Iridescence(0.9f, 0.5f, 0.5f);

            //scene.Objects.AddLast(sphere);

            ColorMap rgbColorMap = new ColorMap(new Dictionary<float, Color> { { 0, new Color(1, 0, 0) }, { 0.5f, new Color(0, 1, 0) }, { 1, new Color(0, 0, 1) } });
            var objectsPigment = Pigment.Wrinkles(rgbColorMap);
            objectsPigment.Turbulence = new Vector3(1, 1, 1) * 0.01f;
            objectsPigment.Transformation = new Scale(0.1f);
            objectsPigment.Waveform = Waveform.Scallop();

            scene.Objects.AddLast(new Sphere(new Vector3(0, 0.5f, 0), 0.5f) { Pigment = objectsPigment });
            scene.Objects.AddLast(new Box(1) { Pigment = objectsPigment, Transformation = new Scale(0.5f) & new Translation(-1, 0.5f, 0) });

            /*
            var csg = new Union();
            csg.Objects.AddLast(new Sphere(new Vector3(0, 1, 0), 1) { Pigment = Color.FromHSB(0.37f, 0.45f, 0.95f) });
            csg.Objects.AddLast(new Sphere(new Vector3(0.7f, 1, 0), 1) { Pigment = Color.FromHSB(0.47f, 0.35f, 0.85f) });
            csg.Transformation.AddScale(0.5f);
            //csg.CutawayTextures = true;
            scene.Objects.AddLast(csg);*/

            scene.WriteToFile();
            scene.Render(povExePath);
        }

        static void Test2() {
            Random random = new Random(47468621);
            Random randomColor = new Random(162588);

            Scene.GlobalOneLiners = false;

            Scene scene = new Scene("test2");

            scene.GlobalSettings = new GlobalSettings(1.0f);
            scene.Background = Color.FromHSB(0, 0, 1);

            var cameraPosOffset = new Vector3(0.4f, 0, 0);
            scene.Camera = new Camera(cameraPosOffset + new Vector3(0, 0.11f, 2) * 0.85f, new Vector3(0, 0.5f, 0));

            scene.LightSources.AddLast(new LightSource(new Vector3(-5, 10, 10), new Color(1, 1, 1)));

            Plane plane = new Plane(new Vector3(0, 1, 0), 0);
            plane.Pigment = Color.FromHSB(0, 0, 0.95f);
            plane.Finish = new Finish() { 
                Reflection = 0.2f,
                Ambient = 0.2f,
            };
            scene.Objects.AddLast(plane);

            int numberOfSlices = 6;
            int numberOfRings = 8;
            float sliceSpace = 0.005f*2;
            float ringSpace = 0.005f*2;
            float cutOverlapHeight = 0.1f;

            Union cylindersUnion = new Union();

            for (int ringIndex = numberOfRings - 1; ringIndex > 1; --ringIndex) {
                for (int sliceIndex = 0; sliceIndex < numberOfSlices; ++sliceIndex) {
                    float sliceHeight = 1.0f / numberOfSlices;

                    float oddSliceHeightOffset = 0;//ringIndex % 2 == 0 ? sliceHeight*0.5f : 0;

                    Cylinder c1 = new Cylinder(
                        new Vector3(0, sliceHeight * sliceIndex + oddSliceHeightOffset, 0),
                        new Vector3(0, sliceHeight * (sliceIndex + 1) - sliceSpace + oddSliceHeightOffset, 0),
                        0.5f / numberOfRings * (ringIndex + 1));

                    random.NextDouble();

                    c1.Pigment = Color.FromHSB(0.025f + (float)randomColor.NextDouble() * 0.13f, 0.999f, 0.85f);
                    c1.Finish = new Finish() {
                        Reflection = 0.05f,
                        Metallic = 1,
                        Specular = 0.5f,
                        Diffuse = 0.99f,
                        //Ambient = 0.5f
                    };


                    Cylinder c2 = new Cylinder(
                        new Vector3(0, -cutOverlapHeight, 0),
                        new Vector3(0, 1 + cutOverlapHeight, 0),
                        0.5f / numberOfRings * (ringIndex + 1 - 1) + ringSpace);

                    Difference d = new Difference(c1, c2) { CutawayTextures = true };

                    if (random.NextDouble() > 0.35f) {
                        d.Transformation = new Translation(0.55f, 0, 0);
                    } else {
                        d.Transformation = new Translation(-0.55f, 0, 0);
                    }

                    cylindersUnion.Objects.AddLast(d);
                }
            }
            //scene.Objects.AddLast(cylindersUnion);

            Box cutBox1 = new Box(new Vector3(1, 1.5f, 1));
            cutBox1.Transformation = new Rotation(0, 45, 0) & new Translation(0 + 0.55f, 0.5f, (float)Math.Sqrt(0.5));
            //scene.Objects.AddLast(cutBox);

            Box cutBox2 = new Box(new Vector3(1, 1.5f, 1));
            cutBox2.Transformation = new Rotation(0, 45, 0) & new Translation(0 - 0.55f, 0.5f, (float)Math.Sqrt(0.5));

            Difference work = new Difference(cylindersUnion, cutBox1, cutBox2) { CutawayTextures = true };
            scene.Objects.AddLast(work);

            scene.WriteToFile();
            scene.Render(povExePath);
        }
    }
}
